﻿using System;
using System.Diagnostics;
using System.Linq;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.ComponentModel;
using System.Runtime.ExceptionServices;
using System.IO;

namespace Quality_Calculator.DataLoader
{
    public class MemoryManager : IDisposable
    {
        // Fields
        private int m_lpBytesRead;

        // Methods
        public MemoryManager(int hwnd)
        {
            this.ProcessHandle = Imports.OpenProcess(Imports.ProcessAccessFlags.All, false, hwnd);
            this.Process = Process.GetProcessById(hwnd);
            this.ThreadHandle = Imports.OpenThread(0x1f03ff, false, (uint)this.Process.Threads[0].Id);
            if (this.ProcessHandle.IsInvalid)
            {
                Console.WriteLine(string.Concat(new object[] { "Failed To Open Process: ", hwnd, ". Error: ", Imports.GetLastError() }), "Error");
            }
        }

        public MemoryManager(IntPtr hwnd)
        {
            uint num;
            Imports.GetWindowThreadProcessId(hwnd, out num);
            this.ProcessHandle = Imports.OpenProcess(Imports.ProcessAccessFlags.All, false, (int)num);
            this.Process = Process.GetProcessById((int)num);
            this.ThreadHandle = Imports.OpenThread(0x1f03ff, false, (uint)this.Process.Threads[0].Id);
            if (this.ProcessHandle.IsInvalid)
            {
                Console.WriteLine(string.Concat(new object[] { "Failed To Open Process: ", hwnd, ". Error: ", Imports.GetLastError() }), "Error");
            }
        }
        public void Dispose()
        {        
            if (this.ThreadHandle != IntPtr.Zero)
            {
                this.ThreadHandle = IntPtr.Zero;
            }
            if (this.ProcessHandle != null)
            {

                this.ProcessHandle.Dispose();
                this.ProcessHandle = null;
            }
        }

        public static uint FindPattern(byte[] bData, byte[] bPattern, string szMask)
        {
            if ((bData == null) || (bData.Length == 0))
            {
                throw new ArgumentNullException("bData");
            }
            if ((bPattern == null) || (bPattern.Length == 0))
            {
                throw new ArgumentNullException("bPattern");
            }
            if (szMask == string.Empty)
            {
                throw new ArgumentNullException("szMask");
            }
            if (bPattern.Length != szMask.Length)
            {
                throw new ArgumentException("Pattern and Mask lengths must be the same.");
            }
            bool flag = false;
            int length = bPattern.Length;
            int num4 = bData.Length - length;
            for (int i = 0; i < num4; i++)
            {
                flag = true;
                for (int j = 0; j < length; j++)
                {
                    if (((szMask[j] == 'x') && (bPattern[j] != bData[i + j])) || ((szMask[j] == '!') && (bPattern[j] == bData[i + j])))
                    {
                        flag = false;
                        break;
                    }
                }
                if (flag)
                {
                    return (uint)i;
                }
            }
            return 0;
        }

        public uint FindPattern(uint dwStart, uint dwEnd, byte[] bPattern, string szMask)
        {
            byte[] buffer;
            if (dwStart > dwEnd)
            {
                throw new ArgumentException("Start Address cannot be bigger than the End Address");
            }
            int bufferLength = (int)(dwEnd - dwStart);
            if ((bPattern == null) || (bPattern.Length == 0))
            {
                throw new ArgumentNullException("bData");
            }
            if (bPattern.Length != szMask.Length)
            {
                throw new ArgumentException("bData and szMask must be of the same size");
            }
            this.ReadMemory(dwStart, bufferLength, out buffer);
            if (buffer == null)
            {
                throw new Exception("Could not read memory in FindPattern.");
            }
            uint num2 = FindPattern(buffer, bPattern, szMask);
            if (num2 == 0)
            {
                return 0;
            }
            return (dwStart + num2);
        }

        public uint FindPatternRetry(uint dwStart, uint dwEnd, byte[] bPattern, string szMask)
        {
            byte[] buffer;
            if (dwStart > dwEnd)
            {
                throw new ArgumentException("Start Address cannot be bigger than the End Address");
            }
            int bufferLength = (int)(dwEnd - dwStart);
            if ((bPattern == null) || (bPattern.Length == 0))
            {
                throw new ArgumentNullException("bData");
            }
            if (bPattern.Length != szMask.Length)
            {
                throw new ArgumentException("bData and szMask must be of the same size");
            }
            this.ReadMemoryRetry(dwStart, bufferLength, out buffer);
            if (buffer == null)
            {
                throw new Exception("Could not read memory in FindPattern.");
            }
            uint num2 = FindPattern(buffer, bPattern, szMask);
            if (num2 == 0)
            {
                return 0;
            }
            return (dwStart + num2);
        }

        public ProcessModule GetProcessModule(string name)
        {
            return this.Process.Modules.Cast<ProcessModule>().FirstOrDefault<ProcessModule>(p => (p.ModuleName == name));
        }

        public static object RawDeserialize(byte[] rawData, int position, Type anyType)
        {
            int cb = Marshal.SizeOf(anyType);
            if (cb > rawData.Length)
            {
                return null;
            }
            IntPtr destination = Marshal.AllocHGlobal(cb);
            Marshal.Copy(rawData, position, destination, cb);
            object obj2 = Marshal.PtrToStructure(destination, anyType);
            Marshal.FreeHGlobal(destination);
            return obj2;
        }

        public static byte[] RawSerialize(object anything)
        {
            int cb = Marshal.SizeOf(anything);
            IntPtr ptr = Marshal.AllocHGlobal(cb);
            Marshal.StructureToPtr(anything, ptr, false);
            byte[] destination = new byte[cb];
            Marshal.Copy(ptr, destination, 0, cb);
            Marshal.FreeHGlobal(ptr);
            return destination;
        }
        public T ReadMemory<T>(IntPtr address) where T : struct
        {
            byte[] buffer;
            this.ReadMemory(address, Marshal.SizeOf(typeof(T)), out buffer);
            return (T)RawDeserialize(buffer, 0, typeof(T));
        }
        public unsafe T[] ReadArray<T>(IntPtr address, int elements, bool isRelative = false) where T : struct
        {
            T[] result = new T[elements];
            int size = Marshal.SizeOf(typeof(T));
            for (int i = 0; i < elements; i++)
            {
                result[i] = ReadMemory<T>(address + i * size);
            }
            return result;
        }
        public unsafe T UnsafeReadPointersToStruct<T>(bool isRelative = false, params IntPtr[] addresses) where T : struct
        {
            if (addresses.Length == 0)
                throw new InvalidOperationException("Cannot read a value from unspecified addresses.");
            if (addresses.Length == 1)
                return this.UnsafeReadStructure<T>(addresses[0]);
            IntPtr num = this.UnsafeReadStructure<IntPtr>(addresses[0]);
            for (int index = 1; index < addresses.Length - 1; ++index)
                num = this.UnsafeReadStructure<IntPtr>(num + (int)addresses[index]);
            return this.UnsafeReadStructure<T>(num + (int)addresses[addresses.Length - 1]);
        }
        public unsafe T UnsafeReadToStruct<T>(IntPtr address) where T : struct
        {
            return UnsafeReadStructure<T>(address);
        }
        private unsafe T privateReadToStruct<T>(IntPtr address) where T : struct
        {
            byte[] buffer = new byte[Marshal.SizeOf(typeof(T))];
            fixed (byte* numRef = buffer)
            {
                if (((buffer = unsafeRead(address, Marshal.SizeOf(typeof(T)))) != null) && (buffer.Length != 0))
                {
                    return (T)RawDeserialize(buffer, 0, typeof(T));
                }
                else
                    return new T();
            }
        }
        public IntPtr GetPtrFromVTable(IntPtr address, int index)
        {
            IntPtr ptr = this.UnsafeReadToStruct<IntPtr>(address);
            return this.UnsafeReadToStruct<IntPtr>(ptr + (index * 4));
        }
        public object ReadMemory(IntPtr address, Type type)
        {
            byte[] buffer;
            this.ReadMemory(address, Marshal.SizeOf(type), out buffer);
            return RawDeserialize(buffer, 0, type);
        }

        public object ReadMemory(uint address, Type type)
        {
            if (address > 0x7fffffff)
            {
                return null;
            }
            return this.ReadMemory((IntPtr)address, type);
        }

        public uint ReadMemory(uint pointer, uint[] offsets)
        {
            uint num = this.ReadMemoryAsUint(pointer);
            foreach (uint num2 in offsets)
            {
                num = this.ReadMemoryAsUint(num + num2);
            }
            return num;
        }

        public bool ReadMemory(int memoryLocation, int bufferLength, out byte[] lpBuffer)
        {
            lpBuffer = new byte[bufferLength];
            if (this.ProcessHandle.IsInvalid || this.ProcessHandle.IsClosed)
            {
                return false;
            }
            if (!Imports.ReadProcessMemory(this.ProcessHandle.DangerousGetHandle(), (IntPtr)memoryLocation, lpBuffer, bufferLength, out this.m_lpBytesRead))
            {
                Console.WriteLine("Failed to read from Address: {0:X}. Error: {1}", memoryLocation, Imports.GetLastError());
                return false;
            }
            return true;
        }

        public bool ReadMemory(IntPtr memoryLocation, int bufferLength, out byte[] lpBuffer)
        {
            lpBuffer = new byte[bufferLength];
            if (this.ProcessHandle.IsInvalid || this.ProcessHandle.IsClosed)
            {
                return false;
            }
            if (!Imports.ReadProcessMemory(this.ProcessHandle.DangerousGetHandle(), memoryLocation, lpBuffer, bufferLength, out this.m_lpBytesRead))
            {
                Console.WriteLine("Failed to read from Address: {0:X}. Error: {1}", memoryLocation, Imports.GetLastError());
                return false;
            }
            if (this.m_lpBytesRead != bufferLength)
            {
                Console.WriteLine("Failed to read the correct amount of bytes");
                return false;
            }
            return true;
        }

        public bool ReadMemory(uint memoryLocation, int bufferLength, out uint uintOut)
        {
            uintOut = 0;
            if (this.ProcessHandle.IsInvalid || this.ProcessHandle.IsClosed)
            {
                return false;
            }
            byte[] lpBuffer = new byte[4];
            if (!Imports.ReadProcessMemory(this.ProcessHandle.DangerousGetHandle(), (IntPtr)memoryLocation, lpBuffer, bufferLength, out this.m_lpBytesRead))
            {
                Console.WriteLine("Failed to read from Address: {0:X}. Error: {1}", memoryLocation, Imports.GetLastError());
                return false;
            }
            uintOut = BitConverter.ToUInt32(lpBuffer, 0);
            return true;
        }

        public bool ReadMemory(uint memoryLocation, int bufferLength, out byte[] lpBuffer)
        {
            lpBuffer = new byte[bufferLength];
            if (memoryLocation > 0x7fffffff)
            {
                return false;
            }
            if (this.ProcessHandle.IsInvalid || this.ProcessHandle.IsClosed)
            {
                return false;
            }
            if (!Imports.ReadProcessMemory(this.ProcessHandle.DangerousGetHandle(), (IntPtr)memoryLocation, lpBuffer, bufferLength, out this.m_lpBytesRead))
            {
                Console.WriteLine("Failed to read from Address: {0:X}. Error: {1}", memoryLocation, Imports.GetLastError());
                return false;
            }
            return true;
        }

        public float ReadMemoryAsFloat(uint location)
        {
            byte[] buffer;
            this.ReadMemory(location, 4, out buffer);
            return BitConverter.ToSingle(buffer, 0);
        }

        public string ReadMemoryAsHex(uint location, int length)
        {
            byte[] buffer;
            this.ReadMemory(location, length, out buffer);
            StringBuilder builder = new StringBuilder();
            string str = "";
            int num = 0;
            foreach (byte num2 in buffer)
            {
                str = num2.ToString("X");
                if (num2 < 0x10)
                {
                    builder.Append("0" + str + " ");
                }
                else
                {
                    builder.Append(str + " ");
                }
                num++;
            }
            return builder.ToString().Trim();
        }

        public int ReadMemoryAsInt(uint location)
        {
            byte[] buffer;
            this.ReadMemory(location, 4, out buffer);
            return BitConverter.ToInt32(buffer, 0);
        }

        public int ReadMemoryAsIntRetry(uint location)
        {
            byte[] buffer;
            this.ReadMemoryRetry(location, 4, out buffer);
            return BitConverter.ToInt32(buffer, 0);
        }

        public string ReadMemoryAsString(uint location, int length)
        {
            byte[] buffer;
            this.ReadMemory(location, length, out buffer);
            return Encoding.ASCII.GetString(buffer, 0, length).TrimEnd(new char[1]);
        }
        public string ReadMemoryAsString(uint location, int length, Encoding encoding)
        {
            byte[] buffer;
            this.ReadMemory(location, length, out buffer);

            return encoding.GetString(buffer, 0, Array.IndexOf<byte>(buffer, 0x00)).TrimEnd(new char[1]);
        }
        public uint ReadMemoryAsUint(uint location)
        {
            byte[] buffer;
            this.ReadMemory(location, 4, out buffer);
            return BitConverter.ToUInt32(buffer, 0);
        }
        public short ReadMemoryAsShort(uint location)
        {
            byte[] buffer;
            this.ReadMemory(location, 2, out buffer);
            return BitConverter.ToInt16(buffer, 0);
        }
        public bool ReadMemoryRetry(uint memoryLocation, int bufferLength, out byte[] lpBuffer)
        {
            bool flag = false;
            lpBuffer = new byte[bufferLength];
            if (memoryLocation > 0x7fffffff)
            {
                return false;
            }
            if (this.ProcessHandle.IsInvalid || this.ProcessHandle.IsClosed)
            {
                return false;
            }
            while (!flag)
            {
                try
                {
                    Imports.ReadProcessMemory(this.ProcessHandle.DangerousGetHandle(), (IntPtr)memoryLocation, lpBuffer, bufferLength, out this.m_lpBytesRead);
                    flag = true;
                }
                catch
                {
                }
            }
            return true;
        }

        public unsafe byte[] unsafeRead(IntPtr address, int count)
        {
            if (count == 0)
                throw new ArgumentException("Cannot read 0 bytes", "count");
            /*if (isRelative)
                address = this.method_2(address);*/
            byte[] numArray = new byte[count];
            fixed (byte* lpBuffer = &numArray[0])
            {
                int lpNumberOfBytesRead;
                if (Imports.ReadProcessMemory(this.ProcessHandle, address, lpBuffer, count, out lpNumberOfBytesRead) && lpNumberOfBytesRead == count)
                    return numArray;
            }
            throw new AccessViolationException(string.Format("Could not read bytes from {0} [{1}]!", (object)address.ToString("X8"), (object)Marshal.GetLastWin32Error()));
        }

        [HandleProcessCorruptedStateExceptions]
        private unsafe T UnsafeReadStructure<T>(IntPtr address) where T : struct
        {
            fixed (byte* numPtr = this.unsafeRead(address, StructureWrapper<T>.int_0))
            {
                address = (IntPtr)((void*)numPtr);
                //return privateReadToStruct<T>(address);
                try
                {
                    if (address == IntPtr.Zero)
                        throw new InvalidOperationException("Cannot retrieve a value at address 0");
                    object obj1;
                    switch (StructureWrapper<T>.typeCode_0)
                    {
                        case TypeCode.Object:
                            if (StructureWrapper<T>.type_0 == typeof(IntPtr))
                            {
                                return (T)((ValueType)(*(IntPtr*)((void*)address)));
                            }
                            if (!StructureWrapper<T>.ContainsMarshalledFields)
                            {
                                T obj2 = default(T);
                                Imports.RtlMoveMemory(StructureWrapper<T>.delegate1_0(ref obj2), (void*)address, StructureWrapper<T>.int_0);
                                return obj2;
                            }
                            else
                            {
                                obj1 = Marshal.PtrToStructure(address, typeof(T));
                                break;
                            }
                        case TypeCode.Boolean:
                            obj1 = (object)(bool)((int)(*(byte*)(void*)address) != 0 ? true : false);
                            break;
                        case TypeCode.Char:
                            obj1 = (object)(char)*(ushort*)(void*)address;
                            break;
                        case TypeCode.SByte:
                            obj1 = (object)*(sbyte*)(void*)address;
                            break;
                        case TypeCode.Byte:
                            obj1 = (object)*(byte*)(void*)address;
                            break;
                        case TypeCode.Int16:
                            obj1 = (object)*(short*)(void*)address;
                            break;
                        case TypeCode.UInt16:
                            obj1 = (object)*(ushort*)(void*)address;
                            break;
                        case TypeCode.Int32:
                            obj1 = (object)(*((int*)((void*)address)));
                            break;
                        case TypeCode.UInt32:
                            obj1 = (object)*(uint*)(void*)address;
                            break;
                        case TypeCode.Int64:
                            obj1 = (object)*(long*)(void*)address;
                            break;
                        case TypeCode.UInt64:
                            obj1 = (object)(ulong)*(long*)(void*)address;
                            break;
                        case TypeCode.Single:
                            obj1 = (object)*(float*)(void*)address;
                            break;
                        case TypeCode.Double:
                            obj1 = (object)*(double*)(void*)address;
                            break;
                        case TypeCode.Decimal:
                            obj1 = (object)*(Decimal*)(void*)address;
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }
                    return (T)obj1;
                }
                catch (AccessViolationException ex)
                {
                    Trace.WriteLine(string.Concat(new object[4]
        {
          (object) "Access Violation on ",
          (object) address,
          (object) " with type ",
          (object) typeof (T).Name
        }));
                    return default(T);
                }
            }
        }       

        // Properties
        private Process Process { get; set; }
        public bool IsProcessOpen
        {
            get
            {
                return (((this.ProcessHandle != null) && !this.ProcessHandle.IsClosed) && !this.ProcessHandle.IsInvalid);
            }
        }
        public bool IsThreadOpen
        {
            get
            {
                return (this.ThreadHandle != IntPtr.Zero);
            }
        }
        private SafeProcessHandle ProcessHandle { get; set; }
        private IntPtr ThreadHandle { get; set; }
    }

}